home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 007a / gmd_200.zip / GMD.C next >
C/C++ Source or Header  |  1991-02-24  |  39KB  |  1,141 lines

  1. /* ************************************************************************
  2.    *                                                                      *
  3.    *                   Grunged Message Delete Utility                     *
  4.    *                                                                      *
  5.    *  This program was compiled using Borland's Turbo-C++ version 1 with  *
  6.    *  the tiny memory model.  On the author's machine it compiles with    *
  7.    *  no errors or warnings.                                              *
  8.    *                                                                      *
  9.    *                                                                      *
  10.    *  Warning:                                                            *
  11.    *                                                                      *
  12.    *  This code uses un-documented features of DOS to perform the fast    *
  13.    *  mode message scanning.  While these un-documented features have     *
  14.    *  been around and not changed, future releases of DOS may change      *
  15.    *  the way these features work.  For reference, the un-documented      *
  16.    *  features used are:                                                  *
  17.    *                                                                      *
  18.    *  1)  This program uses two un-documented fields in the reserved      *
  19.    *      part (the first 21 bytes) of the DTA for the file find first/   *
  20.    *      next DOS function (Int 21h, Functions 4Eh/4Fh).  Those field    *
  21.    *      offsets and their meaning are:                                  *
  22.    *                                                                      *
  23.    *      a)  Word at offset 13                                           *
  24.    *          This is the directory entry number within the directory     *
  25.    *          of the current matching entry.  The directory entry number  *
  26.    *          starts at 0 for the first entry and increases by one for    *
  27.    *          each subsequent entry regardless of how the directory       *
  28.    *          entry is used or not used.  It continues incrementing past  *
  29.    *          sector/cluster boundaries.  This is how the find next       *
  30.    *          functions knows where to pick up the search.                *
  31.    *      b)  Word at offset 15                                           *
  32.    *          This is the cluster number of the first cluster of the      *
  33.    *          directory containing the matching entry.  No matter which   *
  34.    *          directory entry number matches, this is always the first    *
  35.    *          cluster.  When the directory is the root directory, this    *
  36.    *          number is 0.                                                *
  37.    *                                                                      *
  38.    *  2)  This program uses DOS Int 21h Function 32h (Get Drive Parameter *
  39.    *      Block).  On entry, AH=32h and DL=drive number (0=default, 1=A,  *
  40.    *      etc.).  On return, AL contains FFh if the drive was invalid and *
  41.    *      DS:BX points to the data structure described by                 *
  42.    *      'drive_parm_struct' defined below.  The meaning of the fields   *
  43.    *      is is easily discerned from the field name.  A complete         *
  44.    *      description of this function is contained in Terry Dettmann's   *
  45.    *      "DOS Programmer's Reference".                                   *
  46.    *                                                                      *
  47.    *  3)  This program uses the FAT.  It can handle the 12-bit and 16-bit *
  48.    *      formats.  A complete description of the FAT and how it works    *
  49.    *      can also be found in Terry Dettmann's book (see above).         *
  50.    *                                                                      *
  51.    *  Don't play poker with the devil.  If you have a version of DOS      *
  52.    *  which is strange, do not use the '-f' switch.  If you see the       *
  53.    *  warning message 'Note:  Fast mode disabled.' or the program locks   *
  54.    *  up, stop using the '-f' switch.                                     *
  55.    *                                                                      *
  56.    *  David Troendle                                                      *
  57.    *  New Orleans, LA, USA                                                *
  58.    *  November, 1990                                                      *
  59.    ************************************************************************ */
  60.  
  61. /* Maintenance History:
  62.  
  63.    01/26/91 - GMD got confused by -d<areaname> and -date:n.  When it saw
  64.               -date:n, it picked up the "ate:n" as an area.
  65.    02/12/91 - ConfMail and MRen place comments in Areas.Bbs starting with
  66.               a "-".  GMD now detects lines starting with "-" as a comment.
  67.    02/12/91 - GMD created bad filenames when it attempted to move messages
  68.               in an echo name which contained illegal DOS filename
  69.               characters.
  70.       ---------------------------Release 1.01---------------------------
  71.    02/24/91 - Added processing for the EchoToss log.  Only those echos listed
  72.               in the file specified by the -e switch are processed.  Those
  73.               files are processed from the LastRead pointer in 1.MSG in that
  74.               area.
  75. */
  76.  
  77. #include <stdio.h>
  78. #include <stdlib.h>
  79. #include <stdarg.h>
  80. #include <string.h>
  81. #include <ctype.h>
  82. #include <dir.h>
  83. #include <dos.h>
  84. #include <io.h>
  85. #include <conio.h>
  86.  
  87. #define MAX_ECHONAME_LENGTH  256
  88. #define MAX_FILENAME_LENGTH  256
  89. #define MAX_TOKEN_SIZE       2048
  90. #define SMALL_FAT_MAX_CLUSTERS 4087
  91.  
  92. typedef
  93.  struct FIDO_message_attribute_struct
  94.   {
  95.    unsigned                  Private              : 1;
  96.    unsigned                  Crash                : 1;
  97.    unsigned                  Recd                 : 1;
  98.    unsigned                  Sent                 : 1;
  99.    unsigned                  FileAttached         : 1;
  100.    unsigned                  InTransit            : 1;
  101.    unsigned                  Orphan               : 1;
  102.    unsigned                  KillSent             : 1;
  103.    unsigned                  Local                : 1;
  104.    unsigned                  HoldForPickup        : 1;
  105.    unsigned                  unused               : 1;
  106.    unsigned                  FileRequest          : 1;
  107.    unsigned                  ReturnReceiptRequest : 1;
  108.    unsigned                  IsReturnReceipt      : 1;
  109.    unsigned                  AuditRequest         : 1;
  110.    unsigned                  FileUpdateReq        : 1;
  111.   }                          FIDO_message_attrib_type;
  112.  
  113. typedef
  114.  struct FIDO_message_struct
  115.   {
  116.    char                      fromUserName[36];
  117.    char                      toUserName[36];
  118.    char                      subject[72];
  119.    char                      dateTime[20];
  120.    unsigned                  timesRead;
  121.    unsigned                  destNode;
  122.    unsigned                  origNode;
  123.    unsigned                  cost;
  124.    unsigned                  origNet;
  125.    unsigned                  destNet;
  126.    unsigned                  destZone;
  127.    unsigned                  origZone;
  128.    unsigned                  destPoint;
  129.    unsigned                  origPoint;
  130.    unsigned                  replyTo;
  131.    FIDO_message_attrib_type  Attribute;
  132.    unsigned                  nextReply;
  133.   }                          FIDO_message_type;
  134. typedef
  135.  FIDO_message_type *         FIDO_message_ptr_type;
  136.  
  137. typedef
  138.  struct directory_entry_struct
  139.   {
  140.    char                      file[8];
  141.    char                      ext[3];
  142.    unsigned char             attribute;
  143.    char                      reserved[10];
  144.    unsigned                  time;
  145.    unsigned                  date;
  146.    unsigned                  starting_cluster;
  147.    unsigned long             size;
  148.   }                          directory_entry_type;
  149. typedef
  150.  directory_entry_type *      directory_entry_ptr_type;
  151.  
  152. typedef
  153.  struct drive_parm_struct
  154.   {
  155.    unsigned char             drive_number;
  156.    unsigned char             unit_number;
  157.    unsigned                  bytes_per_sector;
  158.    unsigned char             sectors_per_cluster;
  159.    unsigned char             shift_factor;
  160.    unsigned                  reserved_sectors;
  161.    unsigned char             number_of_FAT_copies;
  162.    unsigned                  n_root_directory_entries;
  163.    unsigned                  first_data_sector;
  164.    unsigned                  n_clusters;
  165.    unsigned                  sectors_per_FAT;
  166.   }                          drive_parm_type;
  167. typedef
  168.  drive_parm_type far *       drive_parm_ptr_type;
  169.  
  170. enum token_type_enum
  171.  {
  172.   token_eof,
  173.   token_comment,
  174.   token_eol,
  175.   token_normal
  176.  };
  177.  
  178. typedef
  179.  struct tosslog_list_struct
  180.   {
  181.    struct tosslog_list_struct * next;
  182.    char                      area[1];
  183.   }                          tosslog_list_type;
  184. typedef tosslog_list_type *  tosslog_list_ptr_type;
  185.  
  186. void                         add_token_char(void);
  187. void                         advance_to_next_line(void);
  188. void *                       allocate_memory(unsigned      n_bytes);
  189. void                         check_for_grunged_messages(unsigned from1msg);
  190. void                         check_message(void);
  191. void                         close_file(FILE * *           file);
  192. unsigned long                cluster_to_sector(unsigned    cluster);
  193. unsigned                     current_cluster;
  194. void                         delete_grunged_messages(void);
  195. void                         drop_fast_mode(void);
  196. directory_entry_ptr_type     get_directory_entry(void);
  197. unsigned                     get_message_number(void);
  198. void                         get_next_char(void);
  199. unsigned                     get_token(void);
  200. unsigned                     grunged(char *                s,
  201.                                      unsigned              length,
  202.                                      unsigned              ok_count);
  203. void                         initialize(unsigned           argc,
  204.                                         char *             argv[]);
  205. void                         load_bad_message_table(void);
  206. void                         load_drive_parms(char *       file);
  207. void                         load_tosslog_list(void);
  208. void                         log_message(unsigned          level,
  209.                                          char *            fmt,
  210.                                          ...);
  211. FILE *                       open_file(char *              filename,
  212.                                        char *              mode);
  213. void                         next_cluster(void);
  214. void                         parse_parms(unsigned          argc,
  215.                                          char *            argv[]);
  216. unsigned                     process_echo(void);
  217. void                         process_header(void);
  218. void                         read_cluster(unsigned         cluster);
  219. void                         read_disk(unsigned long       sector,
  220.                                        unsigned            n_sectors,
  221.                                        void *              buffer);
  222. FIDO_message_ptr_type        read_message(void);
  223. void                         read_sector(unsigned long     sector_number);
  224. void                         shut_down(unsigned            return_code);
  225. void                         synchronize_cluster(void);
  226.  
  227. FILE *                       areas_in;
  228. char *                       areas_in_name;
  229. char *                       bad_directory_name;
  230. char                         bad_echo_name[9];
  231. unsigned char                bad_message_table[1000];
  232. unsigned                     bytes_per_sector;
  233. int                          c;
  234. unsigned long                cluster_0_sector;
  235. void *                       cluster_buffer;
  236. unsigned                     cluster_in_buffer;
  237. unsigned                     current_relative_cluster;
  238. unsigned                     current_FAT_sector;
  239. char                         directory[MAX_FILENAME_LENGTH];
  240. unsigned *                   directory_cluster;
  241. unsigned                     directory_entries_per_cluster;
  242. unsigned *                   directory_index;
  243. unsigned                     drive;
  244. drive_parm_ptr_type          drive_parms;
  245. char                         echo_name[MAX_ECHONAME_LENGTH];
  246. unsigned                     fast_mode;
  247. void *                       FAT_buffer;
  248. struct ffblk                 ff_blk;
  249. unsigned long                first_FAT_sector;
  250. unsigned                     found_grunged_message;
  251. union REGS                   inregs;
  252. unsigned                     line_number;
  253. unsigned                     log_level;
  254. FIDO_message_type            message;
  255. char                         message_filename[MAX_FILENAME_LENGTH];
  256. unsigned                     n_clusters;
  257. unsigned                     n_grunged;
  258. unsigned                     ok_date;
  259. unsigned                     ok_from;
  260. unsigned                     ok_subject;
  261. unsigned                     ok_to;
  262. union REGS                   outregs;
  263. void *                       sector_buffer;
  264. unsigned long                sectors_per_cluster;
  265. struct SREGS                 segregs;
  266. unsigned                     skip_passthrus;
  267. FILE *                       temp_file;
  268. char                         temp_filename[13];
  269. unsigned                     test_mode;
  270. char                         token[MAX_TOKEN_SIZE];
  271. char *                       token_end;
  272. unsigned                     token_length;
  273. unsigned                     token_type;
  274. tosslog_list_ptr_type        tosslog_list;
  275. char *                       tosslog_name;
  276. unsigned                     zlDate;
  277. unsigned                     zlFrom;
  278. unsigned                     zlSubject;
  279. unsigned                     zlTo;
  280.  
  281. void                         main (unsigned                argc,
  282.                                    char *                  argv[])
  283. {
  284.  char *                      c;
  285.  unsigned                    i;
  286.  initialize(argc, argv);
  287.  if (areas_in_name)
  288.   {
  289.    areas_in  = open_file(areas_in_name, "r");
  290.    get_next_char();
  291.    process_header();
  292.    while (process_echo());
  293.   };
  294.  for (i=1; i<argc; i++)
  295.   {
  296.    if ((strnicmp(argv[i], "-d", 2) == 0) &&
  297.        (strnicmp(argv[i], "-date:", 6) != 0))
  298.     {
  299.      strcpy(directory, &argv[i][2]);
  300.      strcpy(echo_name, directory);
  301.      c = strrchr(echo_name, '\\');
  302.      if (c++)
  303.       strcpy(echo_name, c);
  304.      check_for_grunged_messages(0u);
  305.     };
  306.   };
  307.  close_file(&areas_in);
  308.  close_file(&temp_file);
  309.  delete_grunged_messages();
  310.  shut_down(n_grunged > 0);
  311. }
  312.  
  313. void                         add_token_char(void)
  314. {
  315.  if (token_length++ < MAX_TOKEN_SIZE)
  316.   *token_end++ = (char) c;
  317.  *token_end = 0x00;
  318.  return;
  319. }
  320.  
  321. void                         advance_to_next_line()
  322. {
  323.  while ((token_type != token_eol) && (token_type != token_eof))
  324.   get_token();
  325.  if (token_type == token_eol)
  326.   get_token();
  327.  return;
  328. };
  329.  
  330. void *                       allocate_memory(unsigned      n_bytes)
  331. {
  332.  void *                      buffer;
  333.  if ((buffer = malloc(n_bytes)) == NULL)
  334.   {
  335.    log_message(0, "Error:  Out of memory.\n");
  336.    shut_down(2);
  337.   };
  338.  return(buffer);
  339. }
  340.  
  341. void                         check_for_grunged_messages(unsigned from1msg)
  342. {
  343.  unsigned                    count;
  344.  unsigned                    current_message_number;
  345.  unsigned                    done;
  346.  char *                      dot;
  347.  unsigned                    lowest_message;
  348.  FILE *                      message_file;
  349.  unsigned                    n_messages;
  350.  log_message(3, "Processing echo:  %16s  .", echo_name);
  351.  lowest_message = 0u;
  352.  if (from1msg)
  353.   {
  354.    strcpy(message_filename, directory);
  355.    strcat(message_filename, "\\1.msg");
  356.    if ((message_file = fopen(message_filename, "rb")) != NULL)
  357.      {
  358.       fread(&message, sizeof(message), 1, message_file);
  359.       close_file(&message_file);
  360.       lowest_message = message.replyTo;
  361.      };
  362.   };
  363.  count = 100u;
  364.  n_messages = 0u;
  365.  found_grunged_message = 0u;
  366.  strcpy(message_filename, directory);
  367.  strcat(message_filename, "\\*.msg");
  368.  if (fast_mode)
  369.   load_drive_parms(message_filename);
  370.  done = findfirst(message_filename, &ff_blk, 0);
  371.  if (!done && fast_mode)
  372.   {
  373.    current_relative_cluster = 0u;
  374.    current_cluster          = *directory_cluster;
  375.   };
  376.  while (!done)
  377.   {
  378.    strcpy(message_filename, directory);
  379.    strcat(message_filename, "\\");
  380.    strcat(message_filename, ff_blk.ff_name);
  381.    dot = strchr(ff_blk.ff_name, '.');
  382.    *dot = '\000';
  383.    current_message_number = atoi(ff_blk.ff_name);
  384.    *dot = '.';
  385.    if (current_message_number > lowest_message)
  386.     check_message();
  387.    n_messages++;
  388.    if (!count--)
  389.     {
  390.      count = 100u;
  391.      if (found_grunged_message)
  392.        log_message(4, "x");
  393.       else
  394.        log_message(4, ".");
  395.      found_grunged_message = 0u;
  396.     };
  397.    if (kbhit())
  398.     {
  399.      if (getche() == 0x03)
  400.       shut_down(2);
  401.     };
  402.    done = findnext(&ff_blk);
  403.   };
  404.  if (kbhit())
  405.   {
  406.    if (getche() == 0x03)
  407.     shut_down(2);
  408.   };
  409.  if (fast_mode)
  410.   {
  411.    free(cluster_buffer);
  412.    cluster_buffer = NULL;
  413.    free(sector_buffer);
  414.    sector_buffer = NULL;
  415.    free(FAT_buffer);
  416.    FAT_buffer = NULL;
  417.   };
  418.  if (found_grunged_message)
  419.    log_message(4, "x");
  420.   else
  421.    log_message(4, ".");
  422.  log_message(4, " (%u messages)", n_messages);
  423.  log_message(3, "\n");
  424.  return;
  425. }
  426.  
  427. void                         check_message()
  428. {
  429.  FIDO_message_ptr_type       msg;
  430.  msg = read_message();
  431.  if (grunged(msg->fromUserName, sizeof(msg->fromUserName), ok_from)    ||
  432.      grunged(msg->toUserName,   sizeof(msg->toUserName),   ok_to)      ||
  433.      grunged(msg->subject,      sizeof(msg->subject),      ok_subject) ||
  434.      grunged(msg->dateTime,     sizeof(msg->dateTime),     ok_date)    ||
  435.      (zlFrom    && (msg->fromUserName[0] == '\000'))                   ||
  436.      (zlTo      && (msg->toUserName[0]   == '\000'))                   ||
  437.      (zlSubject && (msg->subject[0]      == '\000'))                   ||
  438.      (zlDate    && (msg->dateTime[0]     == '\000')))
  439.   {
  440.    found_grunged_message = 1u;
  441.    n_grunged++;
  442.    fprintf(temp_file, "%s,%s\n", message_filename, echo_name);
  443.   };
  444.  return;
  445. }
  446.  
  447.  
  448. void                         close_file(FILE * *           file)
  449. {
  450.  if (*file)
  451.   {
  452.    fclose(*file);
  453.    *file = NULL;
  454.   };
  455.  return;
  456. }
  457.  
  458. unsigned long                cluster_to_sector(unsigned    cluster)
  459. {
  460.  return(cluster_0_sector + (sectors_per_cluster * ((unsigned long) cluster)));
  461. }
  462.  
  463. void                         delete_grunged_messages()
  464. {
  465.  char                        buffer[512];
  466.  char *                      c;
  467.  char *                      echo;
  468.  struct ftime                ft;
  469.  FILE *                      in;
  470.  unsigned                    item_count;
  471.  unsigned                    message_number;
  472.  FILE *                      out;
  473.  char                        filename[MAX_FILENAME_LENGTH];
  474.  temp_file = open_file(temp_filename, "r");
  475.  while (fgets(token, MAX_TOKEN_SIZE, temp_file))
  476.   {
  477.    echo = strchr(token, ',');
  478.    *echo++ = '\000';
  479.    *strchr(echo, '\n') = '\000';
  480.    strcpy(message_filename, token);
  481.    strcpy(echo_name, echo);
  482.    log_message(2, "Message \"%s\" in echo \"%s\" is grunged",
  483.                   message_filename, echo_name);
  484.    memset(echo_name, 0, MAX_ECHONAME_LENGTH);
  485.    for (c = echo_name; *echo; echo++)
  486.     if ( (*echo == '!')                    ||
  487.         ((*echo >= '#') && (*echo <= ')')) ||
  488.          (*echo == '-')                    ||
  489.         ((*echo >= '0') && (*echo <= '9')) ||
  490.          (*echo == '@')                    ||
  491.         ((*echo >= 'A') && (*echo <= 'Z')) ||
  492.         ((*echo >= '^') && (*echo <= '[')) ||
  493.         ((*echo >= ']') && (*echo <= '~')))
  494.       *c++ = *echo;
  495.    if (strlen(echo_name) == 0)
  496.     strcpy(echo_name,"[NULL]");
  497.    if (test_mode)
  498.      log_message(2, ".\n");
  499.     else
  500.      {
  501.       if (bad_directory_name)
  502.         {
  503.          message_number = get_message_number();
  504.          if (message_number < 1000u)
  505.            {
  506.             sprintf(filename, "%s\\%s.%03u",
  507.                     bad_directory_name, bad_echo_name, message_number);
  508.  
  509.             log_message(2, ".\n");
  510.             log_message(2,"  (Moved to %s.)\n", filename);
  511.             in  = open_file(message_filename, "rb");
  512.             out = open_file(filename, "wb");
  513.             while ((item_count = fread(buffer, 1, 512, in)) != 0)
  514.              fwrite(buffer, 1, item_count, out);
  515.             getftime(fileno(in), &ft);
  516.             close_file(&in);
  517.             fflush(out);
  518.             setftime(fileno(out), &ft);
  519.             close_file(&out);
  520.             unlink(message_filename);
  521.            }
  522.           else
  523.            {
  524.             log_message(2, ".\n");
  525.             log_message(2, "  (Deleted because it could not be moved.)\n");
  526.             unlink(message_filename);
  527.            };
  528.         }
  529.        else
  530.         {
  531.          if (unlink(message_filename) == 0)
  532.            log_message(2, " (remove OK).\n");
  533.           else
  534.            log_message(2, " (could not remove).\n");
  535.         };
  536.      };
  537.   };
  538.  close_file(&temp_file);
  539.  return;
  540. }
  541.  
  542. void                         drop_fast_mode()
  543. {
  544.  log_message(0, "\n");
  545.  log_message(0, "Note:  Fast mode disabled\n");
  546.  fast_mode = 0u;
  547.  if (cluster_buffer)
  548.   free(cluster_buffer);
  549.  cluster_buffer = NULL;
  550.  if (sector_buffer)
  551.   free(sector_buffer);
  552.  sector_buffer = NULL;
  553.  if (FAT_buffer)
  554.   free(FAT_buffer);
  555.  FAT_buffer = NULL;
  556.  return;
  557. }
  558.  
  559. directory_entry_ptr_type     get_directory_entry()
  560. {
  561.  directory_entry_ptr_type    dir;
  562.  synchronize_cluster();
  563.  dir = (directory_entry_ptr_type) cluster_buffer;
  564.  return(&dir[*directory_index % directory_entries_per_cluster]);
  565. }
  566.  
  567. unsigned                     get_message_number()
  568. {
  569.  unsigned                    i;
  570.  unsigned char *             up;
  571.  if (strnicmp(echo_name, bad_echo_name, 8) != 0)
  572.   load_bad_message_table();
  573.  for (i = 0u, up = bad_message_table; i < 1000u; i++, up++)
  574.   {
  575.    if (*up == 0)
  576.     {
  577.      *up = 1u;
  578.      return(i);
  579.     };
  580.   };
  581.  return(1000u);
  582. }
  583.  
  584. void                         get_next_char(void)
  585. {
  586.  c = fgetc(areas_in);
  587.  if (c == '\n')
  588.   line_number++;
  589.  return;
  590. }
  591.  
  592. unsigned                     get_token()
  593. {
  594.  token_length = 0u;
  595.  token_end    = token;
  596.  *token_end   = 0x00;
  597.  while ((c == ' ') || (c == 0x09))
  598.   get_next_char();
  599.  if (c == EOF)
  600.   {
  601.    token_type = token_eof;
  602.    return(token_type);
  603.   };
  604.  if ((c == ';') || (c == '%') || (c == '-'))
  605.   {
  606.    token_type = token_comment;
  607.    while ((c!='\n') && (c!=EOF))
  608.     {
  609.      add_token_char();
  610.      get_next_char();
  611.     };
  612.    return(token_type);
  613.   };
  614.  if (c == '\n')
  615.   {
  616.    token_type = token_eol;
  617.    get_next_char();
  618.    return(token_type);
  619.   };
  620.  token_type = token_normal;
  621.  while ((c!=' ') && (c!=0x09) && (c!='\n') && (c!=EOF))
  622.   {
  623.    add_token_char();
  624.    get_next_char();
  625.   };
  626.  return(token_type);
  627. }
  628.  
  629. unsigned                     grunged(char *                s,
  630.                                      unsigned              length,
  631.                                      unsigned              ok_count)
  632. {
  633.  while (*s)
  634.   {
  635.    if ((!length--) || ((*s++ & 0x80) && !ok_count--))
  636.     return(1u);
  637.   };
  638.  return(0u);
  639. }
  640.  
  641. void                         initialize(unsigned           argc,
  642.                                         char *             argv[])
  643. {
  644.  line_number             = 1u;
  645.  log_level               = 4u;
  646.  n_grunged               = 0u;
  647.  fast_mode               = 0u;
  648.  test_mode               = 0u;
  649.  ok_date                 = 0u;
  650.  ok_from                 = 0u;
  651.  ok_subject              = 0u;
  652.  ok_to                   = 0u;
  653.  zlDate                  = 0u;
  654.  zlFrom                  = 0u;
  655.  zlSubject               = 0u;
  656.  zlTo                    = 0u;
  657.  cluster_buffer          = NULL;
  658.  sector_buffer           = NULL;
  659.  FAT_buffer              = NULL;
  660.  areas_in_name           = NULL;
  661.  areas_in                = NULL;
  662.  tosslog_name            = NULL;
  663.  tosslog_list            = NULL;
  664.  temp_file               = NULL;
  665.  directory_index         = (unsigned *) &ff_blk.ff_reserved[13];
  666.  directory_cluster       = (unsigned *) &ff_blk.ff_reserved[15];
  667.  strcpy(bad_echo_name, "");
  668.  tmpnam(temp_filename);
  669.  parse_parms(argc, argv);
  670.  load_tosslog_list();
  671.  if (argc < 2u)
  672.   {
  673.    log_message(0, "\n\n\n\n\n");
  674.    log_message(0, "Usage:  GMD [-a<Areas.Bbs>] [-b<bad_path>] [-e<toss_log>] [-d<directory>]...\n");
  675.    log_message(0, "            [switches]\n");
  676.    log_message(0, "        Where:  <Areas.Bbs> is the name of the QMail Areas.Bbs file.\n");
  677.    log_message(0, "                <directory> is a message area directory to be manually\n");
  678.    log_message(0, "                            checked for grunged messages.\n");
  679.    log_message(0, "                <toss_log>  is a list of areas to process.\n");
  680.    log_message(0, "                <bad_path>  is the path where grunged messages are copied.\n");
  681.    log_message(0, "     Switches:  -date:n    - Permit \'n\' bad characters in \'date\' field.\n");
  682.    log_message(0, "                -from:n    - Permit \'n\' bad characters in \'from\' field.\n");
  683.    log_message(0, "                -subject:n - Permit \'n\' bad characters in \'subject\' field.\n");
  684.    log_message(0, "                -to:n      - Permit \'n\' bad characters in \'to\' field.\n");
  685.    log_message(0, "                -zlDate    - Treat zero length \'date\' fields as grunged.\n");
  686.    log_message(0, "                -zlFrom    - Treat zero length \'from\' fields as grunged.\n");
  687.    log_message(0, "                -zlSubject - Treat zero length \'subject\' fields as grunged.\n");
  688.    log_message(0, "                -zlTo      - Treat zero length \'to\' fields as grunged.\n");
  689.    log_message(0, "                -l:n       - Set logging detail level.\n");
  690.    log_message(0, "                -f - Sets fast mode by bypassing DOS for reads.\n");
  691.    log_message(0, "                -p - Skip passthru areas when -e is specified.\n");
  692.    log_message(0, "                -t - Sets test mode.  No messages are deleted.\n");
  693.    log_message(0, "     Function:  GMD deletes grunged messages from the directories listed\n");
  694.    log_message(0, "                in <Areas.Bbs>.  Additional directories can be manually\n");
  695.    log_message(0, "                specified with -d switches.\n");
  696.    shut_down(2);
  697.   };
  698.  log_message(1, "Grunged Message Delete utility.  Written by David Troendle (1:396/5).\n");
  699.  log_message(1, "Tested, documented and distributed by John Souvestre (1:396/1).\n");
  700.  log_message(1, "Version 2.0, February 24, 1991.\n");
  701.  log_message(1, "\n");
  702.  log_message(1, "Scanning for grunged messages.\n");
  703.  temp_file = open_file(temp_filename, "w");
  704.  return;
  705. }
  706.  
  707. void                         load_bad_message_table()
  708. {
  709.  unsigned                    done;
  710.  char *                      ext;
  711.  char *                      ext2;
  712.  char                        file[MAX_FILENAME_LENGTH];
  713.  unsigned                    i;
  714.  unsigned char *             up;
  715.  for (i = 0u, up = bad_message_table; i < 1000u; i++)
  716.   *up++ = 0;
  717.  memcpy(bad_echo_name, echo_name, 8);
  718.  bad_echo_name[8] = '\000';
  719.  strcpy(file, bad_directory_name);
  720.  strcat(file, "\\");
  721.  strcat(file, bad_echo_name);
  722.  strcat(file, ".*");
  723.  done = findfirst(file, &ff_blk, FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_DIREC);
  724.  while (!done)
  725.   {
  726.    if ((ext = ext2 = strrchr(ff_blk.ff_name, '.')) != NULL)
  727.     {
  728.      ext++;
  729.      ext2++;
  730.      while (*ext)
  731.       {
  732.        if (!isdigit(*ext))
  733.         break;
  734.        ext++;
  735.       };
  736.      if (*ext == '\000')
  737.       bad_message_table[atoi(ext2)] = 1u;
  738.     };
  739.    done = findnext(&ff_blk);
  740.   };
  741.  return;
  742. }
  743.  
  744. void                         load_drive_parms(char *       file)
  745. {
  746.  if (file[1] == ':')
  747.    drive = tolower(file[0]) - 'a';
  748.   else
  749.    drive = getdisk();
  750.  inregs.h.ah = 0x32;
  751.  inregs.h.dl = drive + 1u;
  752.  intdosx(&inregs, &outregs, &segregs);
  753.  if (outregs.h.al == 0xff)
  754.   {
  755.    drop_fast_mode();
  756.    return;
  757.   };
  758.  drive_parms = MK_FP(segregs.ds, outregs.x.bx);
  759.  bytes_per_sector               = drive_parms->bytes_per_sector;
  760.  sectors_per_cluster            =
  761.    (unsigned long) drive_parms->sectors_per_cluster + 1u;
  762.  directory_entries_per_cluster  =
  763.    (bytes_per_sector * ((unsigned) sectors_per_cluster)) /
  764.    sizeof(directory_entry_type);
  765.  n_clusters                     = drive_parms->n_clusters;
  766.  cluster_0_sector               =
  767.    ((unsigned long) drive_parms->first_data_sector) -
  768.    2ul * sectors_per_cluster;
  769.  first_FAT_sector               =
  770.    (unsigned long) drive_parms->reserved_sectors;
  771.  cluster_buffer                 =
  772.    allocate_memory(bytes_per_sector * ((unsigned) sectors_per_cluster));
  773.  FAT_buffer                     = allocate_memory(2u * bytes_per_sector);
  774.  sector_buffer                  = allocate_memory(bytes_per_sector);
  775.  current_FAT_sector             = 0u;
  776.  cluster_in_buffer              = 0u;
  777.  return;
  778. }
  779.  
  780. void                         load_tosslog_list(void)
  781. {
  782.  unsigned                    len;
  783.  tosslog_list_ptr_type       tosslog_entry;
  784.  FILE *                      tosslog_file;
  785.  if (!tosslog_name)
  786.   return;
  787.  if ((tosslog_file = fopen(tosslog_name, "r")) == NULL)
  788.   return;
  789.  while (fgets(token, MAX_TOKEN_SIZE, tosslog_file))
  790.   {
  791.    len = strlen(token);
  792.    if (len)
  793.     {
  794.      if (token[len-1] == '\n')
  795.       token[--len] = '\000';
  796.     };
  797.    if (len)
  798.     {
  799.      tosslog_entry = (tosslog_list_ptr_type)
  800.                       allocate_memory(sizeof(tosslog_list_type) + len);
  801.      strcpy(tosslog_entry->area, token);
  802.      tosslog_entry->next = tosslog_list;
  803.      tosslog_list        = tosslog_entry;
  804.     };
  805.   };
  806.  close_file(&tosslog_file);
  807.  return;
  808. }
  809.  
  810. void                         log_message(unsigned          level,
  811.                                          char *            fmt,
  812.                                          ...)
  813. {
  814.  va_list                     argptr;
  815.  va_start(argptr, fmt);
  816.  if (level <= log_level)
  817.   {
  818.    if (level <= 1)
  819.      vfprintf(stderr, fmt, argptr);
  820.     else
  821.      vprintf(fmt, argptr);
  822.   };
  823.  va_end(argptr);
  824.  return;
  825. }
  826.  
  827. FILE *                       open_file(char *              filename,
  828.                                        char *              mode)
  829. {
  830.  FILE *                      file;
  831.  if ((file = fopen(filename, mode)) == NULL)
  832.   {
  833.    log_message(0, "Error:  Could not open file \"%s\" for mode \"%s\".\n",
  834.           filename, mode);
  835.    shut_down(2);
  836.   };
  837.  return(file);
  838. }
  839.  
  840. void                         next_cluster()
  841. {
  842.  unsigned long               offset;
  843.  unsigned                    sector;
  844.  unsigned                    sector_offset;
  845.  unsigned                    temp;
  846.  if (n_clusters <= SMALL_FAT_MAX_CLUSTERS)
  847.    {
  848.     offset  = ((unsigned long) current_cluster) * 3ul;
  849.     offset /= 2u;
  850.    }
  851.   else
  852.    offset  = ((unsigned long) current_cluster) * 2ul;
  853.  sector         = ((unsigned) offset) / bytes_per_sector;
  854.  sector_offset  = ((unsigned) offset) % bytes_per_sector;
  855.  sector         = sector + ((unsigned) first_FAT_sector);
  856.  if (current_FAT_sector != sector)
  857.   {
  858.    read_disk((unsigned long) sector, 2, FAT_buffer);
  859.    current_FAT_sector = sector;
  860.   };
  861.  if (n_clusters < SMALL_FAT_MAX_CLUSTERS)
  862.    {
  863.     temp = *((unsigned *) (((char *) FAT_buffer) + sector_offset));
  864.     if (current_cluster & 0x0001)
  865.       current_cluster = temp >> 4u;
  866.      else
  867.       current_cluster = temp && 0x0fff;
  868.    }
  869.   else
  870.    current_cluster = *((unsigned *) (((char *) FAT_buffer) + sector_offset));
  871.  current_relative_cluster++;
  872.  return;
  873. }
  874.  
  875. void                         parse_parms(unsigned          argc,
  876.                                          char *            argv[])
  877. {
  878.  unsigned                    i;
  879.  for (i=1; i<argc; i++)
  880.   {
  881.    if (strnicmp(argv[i], "-a", 2) == 0)
  882.     {
  883.      areas_in_name = &argv[i][2];
  884.      continue;
  885.     };
  886.    if (strnicmp(argv[i], "-b", 2) == 0)
  887.     {
  888.      bad_directory_name = &argv[i][2];
  889.      continue;
  890.     };
  891.    if (strnicmp(argv[i], "-date:", 6) == 0)
  892.     {
  893.      ok_date = atoi(argv[i] + 6);
  894.      continue;
  895.     };
  896.    if (strnicmp(argv[i], "-e", 2) == 0)
  897.     {
  898.      tosslog_name = &argv[i][2];
  899.      continue;
  900.     };
  901.    if (strnicmp(argv[i], "-p", 2) == 0)
  902.     {
  903.      skip_passthrus = 1u;
  904.      continue;
  905.     };
  906.    if (strnicmp(argv[i], "-l:", 3) == 0)
  907.     {
  908.      log_level = atoi(argv[i] + 3);
  909.      if (log_level > 4)
  910.       {
  911.        log_message(0, "Invalid error log level \"%s\".\n", argv[i]);
  912.        shut_down(2);
  913.       };
  914.      continue;
  915.     };
  916.    if (strnicmp(argv[i], "-from:", 6) == 0)
  917.     {
  918.      ok_from = atoi(argv[i] + 6);
  919.      continue;
  920.     };
  921.    if (strnicmp(argv[i], "-subject:", 9) == 0)
  922.     {
  923.      ok_subject = atoi(argv[i] + 9);
  924.      continue;
  925.     };
  926.    if (strnicmp(argv[i], "-to:", 4) == 0)
  927.     {
  928.      ok_to = atoi(argv[i] + 4);
  929.      continue;
  930.     };
  931.    if (strnicmp(argv[i], "-d", 2) == 0)
  932.     continue;
  933.    if (stricmp(argv[i], "-f") == 0)
  934.     {
  935.      fast_mode = 1u;
  936.      continue;
  937.     };
  938.    if (stricmp(argv[i], "-t") == 0)
  939.     {
  940.      test_mode = 1u;
  941.      continue;
  942.     };
  943.    if (stricmp(argv[i], "-zlDate") == 0)
  944.     {
  945.      zlDate = 1u;
  946.      continue;
  947.     };
  948.    if (stricmp(argv[i], "-zlFrom") == 0)
  949.     {
  950.      zlFrom = 1u;
  951.      continue;
  952.     };
  953.    if (stricmp(argv[i], "-zlSubject") == 0)
  954.     {
  955.      zlSubject = 1u;
  956.      continue;
  957.     };
  958.    if (stricmp(argv[i], "-zlTo") == 0)
  959.     {
  960.      zlTo = 1u;
  961.      continue;
  962.     };
  963.    log_message(0, "Error:  Unknown switch \"%s\".\n", argv[i]);
  964.    shut_down(2);
  965.   };
  966.  return;
  967. }
  968.  
  969. unsigned                     process_echo()
  970. {
  971.  unsigned                    found;
  972.  unsigned                    passthru_area;
  973.  tosslog_list_ptr_type       tosslog_entry;
  974.  if (token_type == token_eof)
  975.   return(0u);
  976.  if (token_type == token_comment)
  977.   get_token();
  978.  if (token_type == token_eol)
  979.   {
  980.    get_token();
  981.    return(1u);
  982.   };
  983.  passthru_area = token[0] == '#';
  984.  if (passthru_area)
  985.   {
  986.    if (skip_passthrus)
  987.     {
  988.      advance_to_next_line();
  989.      return(1u);
  990.     };
  991.    strcpy(token, &token[1]);
  992.   };
  993.  strcpy(directory, token);
  994.  get_token();
  995.  if (token_type != token_normal)
  996.   {
  997.    log_message(0, "Error:  Invalid syntax on line %u.\n", line_number);
  998.    shut_down(2);
  999.   };
  1000.  strcpy(echo_name, token);
  1001.  found = tosslog_name == NULL;
  1002.  for (tosslog_entry = tosslog_list;
  1003.       !found && (tosslog_entry != NULL);
  1004.       tosslog_entry = tosslog_entry->next)
  1005.   {
  1006.    found = stricmp(echo_name, tosslog_entry->area) == 0;
  1007.   };
  1008.  if ((tosslog_name != NULL) && found)
  1009.    check_for_grunged_messages(!passthru_area);
  1010.   else
  1011.    if ((tosslog_name == NULL) && !passthru_area)
  1012.      check_for_grunged_messages(0u);
  1013.  advance_to_next_line();
  1014.  return(1u);
  1015. }
  1016.  
  1017. void                         process_header()
  1018. {
  1019.  unsigned                    title_encountered;
  1020.  title_encountered = 0u;
  1021.  while (((title_encountered && ((c == ';') || (c == '\n'))) ||
  1022.           !title_encountered) && (c != EOF))
  1023.   {
  1024.    if (!title_encountered && ((c != ';') && (c != '\n')))
  1025.     title_encountered = 1u;
  1026.    while ((c != '\n') && (c != EOF))
  1027.     get_next_char();
  1028.    if (c != EOF)
  1029.     get_next_char();
  1030.   };
  1031.  get_token();
  1032.  return;
  1033. }
  1034.  
  1035. void                         read_cluster(unsigned         cluster)
  1036. {
  1037.  if (cluster == cluster_in_buffer)
  1038.   return;
  1039.  read_disk(cluster_to_sector(cluster),
  1040.            (unsigned) sectors_per_cluster,
  1041.            cluster_buffer);
  1042.  cluster_in_buffer = cluster;
  1043.  return;
  1044. }
  1045.  
  1046. void                         read_disk(unsigned long       sector,
  1047.                                        unsigned            n_sectors,
  1048.                                        void *              buffer)
  1049. {
  1050.  struct
  1051.   {
  1052.    unsigned long             sector_number;
  1053.    unsigned                  n_sectors;
  1054.    void far *                buffer;
  1055.   }                          extended_parm_block;
  1056.  inregs.h.al = drive;
  1057.  if (sector > 65535ul)
  1058.    {
  1059.     inregs.x.cx                       = 0xffff;
  1060.     extended_parm_block.sector_number = sector;
  1061.     extended_parm_block.n_sectors     = n_sectors;
  1062.     extended_parm_block.buffer        = buffer;
  1063.     inregs.x.bx                       = FP_OFF(&extended_parm_block);
  1064.     segregs.ds                        = FP_SEG(&extended_parm_block);
  1065.    }
  1066.   else
  1067.    {
  1068.     inregs.x.cx                       = n_sectors;
  1069.     inregs.x.dx                       = (unsigned) sector;
  1070.     inregs.x.bx                       = FP_OFF(buffer);
  1071.     segregs.ds                        = FP_SEG(buffer);
  1072.    };
  1073.  int86x(0x25, &inregs, &outregs, &segregs);
  1074.  if (outregs.x.cflag)
  1075.   {
  1076.    log_message(0, "Error:  Could not read sector %lu on drive %c:\n",
  1077.            sector, drive + 'a');
  1078.    shut_down(2);
  1079.   };
  1080.  return;
  1081. }
  1082.  
  1083. #define ROOT_DIRECTORY       (*directory_cluster == 0)
  1084. FIDO_message_ptr_type        read_message()
  1085. {
  1086.  directory_entry_ptr_type    dir;
  1087.  char *                      dot;
  1088.  char                        file[32];
  1089.  FILE *                      message_file;
  1090.  strcpy(file, ff_blk.ff_name);
  1091.  if ((dot = strrchr(file, '.')) != NULL)
  1092.   *dot = '\000';
  1093.  strcat(file,"        ");
  1094.  if (fast_mode && (!ROOT_DIRECTORY))
  1095.   {
  1096.    dir = get_directory_entry();
  1097.    if ((dir->time != ff_blk.ff_ftime) ||
  1098.        (dir->date != ff_blk.ff_fdate) ||
  1099.        (dir->size != ff_blk.ff_fsize) ||
  1100.        (memicmp(dir->file, file, 8) != 0))
  1101.      {
  1102.       drop_fast_mode();
  1103.      }
  1104.     else
  1105.      {
  1106.       read_sector(cluster_to_sector(dir->starting_cluster));
  1107.       return((FIDO_message_ptr_type) sector_buffer);
  1108.      };
  1109.   };
  1110.  message_file = open_file(message_filename, "rb");
  1111.  fread(&message, sizeof(message), 1, message_file);
  1112.  close_file(&message_file);
  1113.  return(&message);
  1114. }
  1115. #undef ROOT_DIRECTORY
  1116.  
  1117. void                         read_sector(unsigned long     sector_number)
  1118. {
  1119.  read_disk(sector_number, 1, sector_buffer);
  1120.  return;
  1121. }
  1122.  
  1123. void                         shut_down(unsigned            return_code)
  1124. {
  1125.  close_file(&temp_file);
  1126.  unlink(temp_filename);
  1127.  close_file(&areas_in);
  1128.  exit(return_code);
  1129.  return;
  1130. }
  1131.  
  1132. void                         synchronize_cluster()
  1133. {
  1134.  unsigned                    required_relative_cluster;
  1135.  required_relative_cluster = *directory_index / directory_entries_per_cluster;
  1136.  while (current_relative_cluster < required_relative_cluster)
  1137.   next_cluster();
  1138.  read_cluster(current_cluster);
  1139.  return;
  1140. }
  1141.